package qe.spider.trade.huaxin;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.NamedThreadFactory;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.tora.traderapi.*;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import qe.spider.MainSelect;
import qe.spider.cfg.Others;
import qe.spider.cfg.TradeConfig;
import qe.spider.cfg.TradeHuaXinCfg;
import qe.spider.trade.StockTrade;
import qe.spider.trade.vo.*;
import qe.spider.vo.Stock;
import qe.spider.vo.StockList;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Slf4j
public class StockTradeHuaXin extends CTORATstpTraderSpi implements StockTrade {

    private static Executor executor = ThreadUtil.newFixedExecutor(100, "华鑫交易", false);//Executors.newFixedThreadPool(100, new NamedThreadFactory("华鑫交易",false));

    public static void initHuaXinTrade() {
        executor.execute(() -> {
            for (TradeHuaXinCfg huaXinCfg : Optional.ofNullable(MainSelect.factor.getOthers()).map(Others::getTradeConfig).map(TradeConfig::getTradeHuaXinCfgList).orElse(new ArrayList<>())) {
                try {
                    StockTradeHuaXin.buildAccounts(huaXinCfg);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    private static StockTradeHuaXin init(TradeHuaXinCfg cfg) {
        System.loadLibrary("./lib/huaxin/x64/javatraderapi");
        CTORATstpTraderApi api = CTORATstpTraderApi.CreateTstpTraderApi();
        StockTradeHuaXin spi = new StockTradeHuaXin(api, cfg);
        api.RegisterSpi(spi);
        api.RegisterFront("tcp://210.14.72.21:4400");
        api.SubscribePrivateTopic(TORA_TE_RESUME_TYPE.TORA_TERT_RESTART);
        api.SubscribePublicTopic(TORA_TE_RESUME_TYPE.TORA_TERT_RESTART);
        api.Init();
        return spi;
    }

    public static Map<String, StockTradeHuaXin> accounts = new ConcurrentHashMap<>();

    private static void buildAccounts(TradeHuaXinCfg cfg) {
        if (accounts.containsKey(cfg.getUserId())) {
            TradeHuaXinCfg source = accounts.get(cfg.getUserId()).cfg;
            if (source.equals(cfg)) return;
            if (!source.getUserId().equals(cfg.getUserId()) || !source.getPassword().equals(cfg.getPassword())) {
                log.warn("华鑫账户和密码不能随意变更");
                return;
            }
            accounts.get(cfg.getUserId()).cfg = cfg;
        }
        StockTradeHuaXin trade = init(cfg);
        accounts.put(cfg.getUserId(), trade);
        new Timer().scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    trade.queryFund();
                    trade.queryOrder();
                    trade.queryPositions();
                } catch (Exception e) {
                    log.error("华鑫查询资金和持仓错误", e);
                }
            }
        }, 10, 1000 * 60 * 10);
    }

    @Getter
    private TradeHuaXinCfg cfg;
    /**
     * UserID是客户号，如果是实盘请填写12位的客户号，如果是模拟账号则使用N视界提供的8位的交易编码
     */
    private String UserID;
    private String Password;
    /**
     * InvestorID同UserID
     */
    private String InvestorID;
    /**
     * AccountID是资金账号，资金账号可以调用ReqQryTradingAccount查询
     */
    private String AccountID;
    /**
     * 上海ShareholderID是股东帐号，可以调用ReqQryShareholderAccount查询
     */
    private String SSEShareholderID;
    /**
     * 深圳ShareholderID是股东帐号，可以调用ReqQryShareholderAccount查询
     */
    private String SZSEShareholderID;
    /**
     * 北京ShareholderID是股东帐号，可以调用ReqQryShareholderAccount查询
     */
    private String BSEShareholderID;

    CTORATstpTraderApi m_api;
    int m_request_id;

    private Boolean isConnected = false;
    private Boolean isLogin = false;

    private Funds funds;
    private List<Order> orders = new ArrayList<>();
    private Boolean orderFinished = false;
    private List<Position> positions = new ArrayList<>();
    private Boolean positionsFinished = false;
    private Map<String, Stock> sellStocks = new HashMap<>();

    public StockTradeHuaXin(CTORATstpTraderApi api, TradeHuaXinCfg cfg) {
        m_api = api;
        m_request_id = 0;
        this.cfg = cfg;
        UserID = cfg.getUserId();
        Password = cfg.getPassword();
        InvestorID = cfg.getUserId();
    }

    public void OnFrontConnected() {
        isConnected = true;
        log.info("华鑫已连接");
        // 连接成功，尝试登陆
        login();
    }

    public void OnFrontDisconnected(int nReason) {
        isConnected = false;
        isLogin = false;
        log.info("华鑫连接已断开----{}", ErrorsHuaXin.getErrMsg(nReason));
    }

    public void OnRspUserLogin(CTORATstpRspUserLoginField pRspUserLoginField, CTORATstpRspInfoField pRspInfo, int nRequestID) {
        if (pRspInfo.getErrorID() == 0 || pRspInfo.getErrorID() == 356) { //356为重复登录
            isLogin = true;
            log.info("【{}（{}）】华鑫登录成功", cfg.getUserName(), pRspUserLoginField.getUserID());

            // 查询股东账号
            CTORATstpQryShareholderAccountField qry_shareholder_account_field = new CTORATstpQryShareholderAccountField();
            //qry_shareholder_account_field.setExchangeID(traderapi.getTORA_TSTP_EXD_SSE());
            qry_shareholder_account_field.setInvestorID(InvestorID);
            int ret = m_api.ReqQryShareholderAccount(qry_shareholder_account_field, ++m_request_id);
            if (ret != 0) {
                log.error("【{}（{}）】华鑫登录后查询股东账户失败----ret[{}]", cfg.getUserName(), pRspUserLoginField.getUserID(), ret);
            }
        } else {
            isLogin = false;
            log.info("【{}（{}）】华鑫登录失败----{}--{}", cfg.getUserName(), UserID, pRspInfo.getErrorID(), pRspInfo.getErrorMsg());
//            if (pRspInfo.getErrorID() != 356 && pRspInfo.getErrorID() != 258) login();  //356为重复登录，258为找不到用户
        }
    }

    public Boolean login() {
        isLogin = false;
        log.info("【{}（{}）】华鑫登录......\n", cfg.getUserName(), UserID);
        CTORATstpReqUserLoginField req_user_login_field = new CTORATstpReqUserLoginField();

        req_user_login_field.setLogInAccount(UserID);
        req_user_login_field.setLogInAccountType(traderapi.getTORA_TSTP_LACT_UserID());
        req_user_login_field.setPassword(Password);
        req_user_login_field.setUserProductInfo("javademo");

        int ret = m_api.ReqUserLogin(req_user_login_field, ++m_request_id);
        if (ret == 0 || ret == 356) {//356是重复登录
            isLogin = true;
            return true;
        }
        log.error("【{}（{}）】华鑫登录错误----{}\n", cfg.getUserName(), UserID, ErrorsHuaXin.getErrMsg(ret));
        return false;
    }

    /**
     * 查询资金股票
     */
    public Funds queryFund() {
//        funds = null;
        if (Boolean.FALSE.equals(isConnected)) {
            log.error("华鑫服务未连接");
            return funds;
        }
        int cnt = 0;
        while (Boolean.FALSE.equals(isLogin) && cnt < 10) {
            log.warn("华鑫服务未登录，再次发起登录...");
            login();
            cnt++;
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        // 查询资金账号
        CTORATstpQryTradingAccountField qry_trading_account_field = new CTORATstpQryTradingAccountField();
        qry_trading_account_field.setInvestorID(InvestorID);
//        qry_trading_account_field.setAccountID(AccountID);

        int ret = m_api.ReqQryTradingAccount(qry_trading_account_field, ++m_request_id);
        if (ret != 0) {
            log.error("华鑫查询资金账号失败----{}", ErrorsHuaXin.getErrMsg(ret));
            return funds;
        }
        CompletableFuture<Funds> future = CompletableFuture.supplyAsync(() -> {
            while (funds == null) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (funds != null) return funds;
            }
            return funds;
        }, executor);
        try {
            return future.get(1, TimeUnit.SECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            log.error("华鑫查询资金账号超时", e);
        }
        return funds;
    }

    /**
     * 查询今日委托
     */
    public List<Order> queryOrder() {
//        orders = new ArrayList<>();
        orderFinished = false;
        if (Boolean.FALSE.equals(isConnected)) {
            log.error("华鑫服务未连接");
            return orders;
        }
        int cnt = 0;
        while (Boolean.FALSE.equals(isLogin) && cnt < 10) {
            log.warn("华鑫服务未登录，再次发起登录...");
            login();
            cnt++;
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 查询报单
        CTORATstpQryOrderField qry_order_field = new CTORATstpQryOrderField();
        qry_order_field.setInvestorID(InvestorID);
        //qry_order_field.setSecurityID("600000");
        //qry_order_field.setInsertTimeStart("09:35:00");
        //qry_order_field.setInsertTimeEnd("10:30:00");
        int ret = m_api.ReqQryOrder(qry_order_field, ++m_request_id);
        if (ret != 0) {
            log.error("【{}（{}）】华鑫查询当日委托失败----{}", cfg.getUserName(), UserID, ErrorsHuaXin.getErrMsg(ret));
            return orders;
        }
        CompletableFuture<List<Order>> future = CompletableFuture.supplyAsync(() -> {
            while (!orderFinished) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (orderFinished) return orders;
            }
            return orders;
        }, executor);
        try {
            return future.get(5, TimeUnit.SECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            log.error("华鑫查询当日委托超时", e);
        }
        return orders;
    }

    /**
     * 买入委托
     */
    public Boolean buy(Stock stock) {
        if (Boolean.FALSE.equals(isConnected)) {
            log.error("华鑫服务未连接");
            return false;
        }
        int cnt = 0;
        while (Boolean.FALSE.equals(isLogin) && cnt < 10) {
            log.warn("华鑫服务未登录，再次发起登录...");
            login();
            cnt++;
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (funds == null) queryFund();
        Integer maxBuyCntPerDay = cfg.getMaxBuyCntPerDay() != null ? cfg.getMaxBuyCntPerDay() : Optional.ofNullable(cfg.getMaxBuyCntPerDay()).isPresent() ? cfg.getMaxBuyCntPerDay() : Optional.ofNullable(MainSelect.factor.getOthers()).map(Others::getTradeConfig).map(TradeConfig::getMaxBuyCntPerDay).orElse(5);
        Collection<Order> todayOrders = orders.stream().filter(o -> o.getOrderType() == OrderType.Buy && DateUtil.isSameDay(o.getOrderTime(), new Date())).collect(Collectors.toSet());
        if (todayOrders.size() >= maxBuyCntPerDay) {
            log.error("今日买入委托数量：" + todayOrders.size() + " 已达上限：" + maxBuyCntPerDay);
            return false;
        }
        if (!stock.currentPrice().isPresent()) {
            log.error("没有" + stock.getStock_code() + "的价格");
            return false;
        }
        // 请求报单
        CTORATstpInputOrderField input_order_field = new CTORATstpInputOrderField();

        input_order_field.setExchangeID("SH".equals(stock.getMarket()) ? traderapi.getTORA_TSTP_EXD_SSE() : traderapi.getTORA_TSTP_EXD_SZSE());

        input_order_field.setSecurityID(stock.getStock_code());
        input_order_field.setShareholderID("SH".equals(stock.getMarket()) ? SSEShareholderID : SZSEShareholderID);
        input_order_field.setDirection(traderapi.getTORA_TSTP_D_Buy());
        BigDecimal p = stock.currentPrice().get().multiply(new BigDecimal("1.015")).setScale(3, RoundingMode.HALF_UP);
        if (stock.getZhangTingJia() != null && NumberUtil.isGreaterOrEqual(p, stock.getZhangTingJia()))
            p = stock.getZhangTingJia();
        Integer volume = buyVolume(funds == null ? new BigDecimal("40000") : (NumberUtil.isGreater(funds.getTotal().divide(BigDecimal.valueOf(maxBuyCntPerDay), 2, RoundingMode.HALF_UP), funds.getAvailable()) ? funds.getAvailable() : funds.getTotal().divide(BigDecimal.valueOf(maxBuyCntPerDay), 2, RoundingMode.HALF_UP)), p);
        input_order_field.setVolumeTotalOriginal(volume);

        //限价买入
//                input_order_field.setLimitPrice(8.57);
//                input_order_field.setOrderPriceType(traderapi.getTORA_TSTP_OPT_LimitPrice());
//                input_order_field.setTimeCondition(traderapi.getTORA_TSTP_TC_GFD());
//                input_order_field.setVolumeCondition(traderapi.getTORA_TSTP_VC_AV());

        //循环委托
        input_order_field.setLimitPrice(p.doubleValue());
        input_order_field.setOrderPriceType(traderapi.getTORA_TSTP_OPT_FiveLevelPrice());
        input_order_field.setTimeCondition(traderapi.getTORA_TSTP_TC_IOC());
        input_order_field.setVolumeCondition(traderapi.getTORA_TSTP_VC_AV());

        int ret = m_api.ReqOrderInsert(input_order_field, ++m_request_id);
        if (ret != 0) {
            log.error("【{}（{}）】华鑫买入委托失败：{}({}) 价格：{}，数量：{}----{}", cfg.getUserName(), UserID, stock.getStock_code(), stock.getStock_name(), input_order_field.getLimitPrice(), input_order_field.getVolumeTotalOriginal(), ret);
            return false;
        } else {
            log.info("【{}（{}）】华鑫买入委托（仓位：1/{}）----{}({}) 价格：{}，数量：{}，账户：{}----总需金额：{}", cfg.getUserName(), UserID, maxBuyCntPerDay, input_order_field.getSecurityID(), stock.getStock_name(), input_order_field.getLimitPrice(), input_order_field.getVolumeTotalOriginal(), input_order_field.getShareholderID(), p.multiply(new BigDecimal(volume)));
            if (orders == null) orders = new ArrayList<>();
            orders.add(Order.builder()
                    .userId(InvestorID)
                    .orderType(OrderType.Buy)
                    .code(stock.getStock_code())
                    .price(p)
                    .volume(new BigDecimal(volume))
                    .orderTime(new Date())
                    .status(OrderStatus.Submitted)
                    .build());
        }
        executor.execute(this::queryOrder);// 买入后立即查询委托
        executor.execute(this::queryPositions);// 买入后立即查询持仓
        return true;
    }

    @Override
    public void buyCancel(Stock stock) {
        Collection<Order> os = orders.stream().filter(p -> p.getOrderType() == OrderType.Buy && p.getCode().equals(stock.getStock_code()) && !StrUtil.isEmpty(p.getSysID())).collect(Collectors.toSet());
        os.forEach(o -> {
            try {
                cancel(o.getSysID(), stock);
            } catch (Exception e) {
                log.error("【{}（{}）】华鑫买入撤单请求错误----{}:{}", cfg.getUserName(), UserID, stock.codeName(), o.getSysID(), e);
            }
        });
    }

    @Override
    public void sellCancel(Stock stock) {
        Collection<Order> os = orders.stream().filter(p -> p.getOrderType() == OrderType.Sell && p.getCode().equals(stock.getStock_code()) && !StrUtil.isEmpty(p.getSysID())).collect(Collectors.toSet());
        os.forEach(o -> {
            try {
                cancel(o.getSysID(), stock);
            } catch (Exception e) {
                log.error("【{}（{}）】华鑫卖出撤单请求错误----{}:{}", cfg.getUserName(), UserID, stock.codeName(), o.getSysID(), e);
            }
        });
    }

    private Boolean cancel(String sysID, Stock stock) {
        if (Boolean.FALSE.equals(isConnected)) {
            log.error("华鑫服务未连接");
            return false;
        }
        int cnt = 0;
        while (Boolean.FALSE.equals(isLogin) && cnt < 10) {
            log.warn("华鑫服务未登录，再次发起登录...");
            login();
            cnt++;
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        CTORATstpInputOrderActionField input_order_action_field = new CTORATstpInputOrderActionField();
        input_order_action_field.setExchangeID("SH".equals(stock.getMarket()) ? traderapi.getTORA_TSTP_EXD_SSE() : traderapi.getTORA_TSTP_EXD_SZSE());
        input_order_action_field.setActionFlag(traderapi.getTORA_TSTP_AF_Delete());
        input_order_action_field.setOrderSysID(sysID);

        int ret = m_api.ReqOrderAction(input_order_action_field, ++m_request_id);
        if (ret != 0) {
            log.warn("【{}（{}）】华鑫请求撤单失败 ReqOrderAction fail----{}:{}, ret[{}]\n", cfg.getUserName(), UserID, stock.codeName(), sysID, ret);
            return false;
        } else {
            log.info("【{}（{}）】华鑫请求撤单----{}:{}", cfg.getUserName(), UserID, stock.codeName(), sysID);
            return true;
        }
    }

    /**
     * 卖出委托
     */
    public Boolean sell(Stock stock) {
        if (Boolean.FALSE.equals(isConnected)) {
            log.error("华鑫服务未连接");
            return false;
        }
        int cnt = 0;
        while (Boolean.FALSE.equals(isLogin) && cnt < 10) {
            log.warn("华鑫服务未登录，再次发起登录...");
            login();
            cnt++;
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (!stock.currentPrice().isPresent()) {
            log.error("没有" + stock.getStock_code() + "的价格");
            return false;
        }
        if (positions == null) {
            positions = queryPositions();
        }
        Optional<Position> op = positions.stream().filter(p -> p.getCode().equals(stock.getStock_code())).findFirst();
        if (!op.isPresent()) {
            log.warn("没有查到" + stock.getStock_code() + "的持仓");
            return false;
        }
        // 请求报单
        CTORATstpInputOrderField input_order_field = new CTORATstpInputOrderField();
        String uuid = IdUtil.nanoId(32);
        sellStocks.put(uuid, stock);
        input_order_field.setSInfo(uuid);
        input_order_field.setExchangeID("SH".equals(stock.getMarket()) ? traderapi.getTORA_TSTP_EXD_SSE() : traderapi.getTORA_TSTP_EXD_SZSE());

        input_order_field.setSecurityID(stock.getStock_code());
        input_order_field.setShareholderID("SH".equals(stock.getMarket()) ? SSEShareholderID : SZSEShareholderID);
        input_order_field.setDirection(traderapi.getTORA_TSTP_D_Sell());
        input_order_field.setVolumeTotalOriginal(op.get().getAvailable());

        //循环委托
        BigDecimal p = stock.currentPrice().get().multiply(new BigDecimal("0.985")).setScale(3, RoundingMode.HALF_UP);
        if (stock.getDieTingJia() != null && NumberUtil.isLessOrEqual(p, stock.getDieTingJia()))
            p = stock.getDieTingJia();
        input_order_field.setLimitPrice(p.doubleValue());
        input_order_field.setOrderPriceType(traderapi.getTORA_TSTP_OPT_FiveLevelPrice());
        input_order_field.setTimeCondition(traderapi.getTORA_TSTP_TC_IOC());
        input_order_field.setVolumeCondition(traderapi.getTORA_TSTP_VC_AV());

        int ret = m_api.ReqOrderInsert(input_order_field, ++m_request_id);
        executor.execute(this::queryOrder);// 卖出后立即查询委托
        executor.execute(this::queryPositions);// 卖出后立即查询持仓
        if (ret != 0) {
            log.error("【{}（{}）】华鑫卖出委托失败：{}({}) 价格：{}，数量：{}----{}", cfg.getUserName(), UserID, stock.getStock_code(), stock.getStock_name(), input_order_field.getLimitPrice(), input_order_field.getVolumeTotalOriginal(), ret);
            return false;
        } else {
            log.info("【{}（{}）】华鑫卖出委托----{}:{}({}) 价格：{}，数量：{},金额：{}", cfg.getUserName(), UserID, input_order_field.getExchangeID(), stock.getStock_code(), stock.getStock_name(), input_order_field.getLimitPrice(), input_order_field.getVolumeTotalOriginal(), input_order_field.getLimitPrice() * input_order_field.getVolumeTotalOriginal());
            if (orders == null) orders = new ArrayList<>();
            orders.add(Order.builder()
                    .userId(InvestorID)
                    .orderType(OrderType.Sell)
                    .code(stock.getStock_code())
                    .price(p)
                    .volume(new BigDecimal(op.get().getAvailable()))
                    .orderTime(new Date())
                    .status(OrderStatus.Submitted)
                    .build());
            return true;
        }
    }

    private Integer buyVolume(BigDecimal cash, BigDecimal price) {
        Integer volume = cash.divide(price.multiply(BigDecimal.valueOf(100)), 2, RoundingMode.HALF_UP).intValue() * 100;
        log.info("【{}（{}）】华鑫买入资金：{}，买入价格：{}，买入股数：{}----资金股票：{}", cfg.getUserName(), UserID, cash, price, volume, funds);
        return volume;
    }

    /**
     * 查询持仓
     */
    public List<Position> queryPositions() {
//        positions = new ArrayList<>();
        positionsFinished = false;
        if (Boolean.FALSE.equals(isConnected)) {
            log.error("华鑫服务未连接");
            return positions;
        }
        int cnt = 0;
        while (Boolean.FALSE.equals(isLogin) && cnt < 10) {
            log.warn("华鑫服务未登录，再次发起登录...");
            login();
            cnt++;
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 查询持仓
        CTORATstpQryPositionField qry_position_field = new CTORATstpQryPositionField();

        qry_position_field.setInvestorID(InvestorID);

        int ret = m_api.ReqQryPosition(qry_position_field, ++m_request_id);
        if (ret != 0) {
            log.error("【{}（{}）】华鑫查询持仓失败----{}", cfg.getUserName(), UserID, ErrorsHuaXin.getErrMsg(ret));
            return positions;
        }
        CompletableFuture<List<Position>> future = CompletableFuture.supplyAsync(() -> {
            while (!positionsFinished) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (positionsFinished) return positions;
            }
            return positions;
        }, executor);
        try {
            return future.get(5, TimeUnit.SECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            log.error("华鑫查询持仓超时", e);
        }
        return positions;
    }


    public void OnRspQrySecurity(CTORATstpSecurityField pSecurity, CTORATstpRspInfoField pRspInfo, int nRequestID, boolean bIsLast) {
        if (pSecurity != null) {
            log.info("【{}（{}）】华鑫查询合约返回----OnRspQrySecurity[{}]: SecurityID[{}] SecurityName[{}] UpperLimitPrice[{}] LowerLimitPrice[{}]\n", cfg.getUserName(), UserID,
                    nRequestID, pSecurity.getSecurityID(), pSecurity.getSecurityName(),
                    pSecurity.getUpperLimitPrice(), pSecurity.getLowerLimitPrice());
        }

        if (bIsLast) {
            log.info("查询合约信息结束!\n");
        }
    }

    public void OnRspQryInvestor(CTORATstpInvestorField pInvestor, CTORATstpRspInfoField pRspInfo, int nRequestID, boolean bIsLast) {
        if (pInvestor != null) {
            log.info("【{}（{}）】华鑫OnRspQryInvestor[{}]: InvestorID[{}] InvestorName[{}] Operways[{}]\n", cfg.getUserName(), UserID,
                    nRequestID, pInvestor.getInvestorID(), pInvestor.getInvestorName(),
                    pInvestor.getOperways());
        }

        if (bIsLast) {
            log.info("华鑫查询投资者信息结束!\n");
        }
    }

    public void OnRspQryShareholderAccount(CTORATstpShareholderAccountField pShareholderAccount, CTORATstpRspInfoField pRspInfo, int nRequestID, boolean bIsLast) {
        if (pShareholderAccount != null) {
            log.info("【{}（{}）】华鑫查询股东账号返回----OnRspQryShareholderAccount[{}]: InvestorID[{}] ExchangeID[{}] MarketID[{}] ShareholderID[{}]\n", cfg.getUserName(), UserID,
                    nRequestID, pShareholderAccount.getInvestorID(), pShareholderAccount.getExchangeID(),
                    pShareholderAccount.getMarketID(), pShareholderAccount.getShareholderID());

            if (pShareholderAccount.getExchangeID() == traderapi.getTORA_TSTP_EXD_SSE())
                SSEShareholderID = pShareholderAccount.getShareholderID();
            else if (pShareholderAccount.getExchangeID() == traderapi.getTORA_TSTP_EXD_SZSE())
                SZSEShareholderID = pShareholderAccount.getShareholderID();
            else if (pShareholderAccount.getExchangeID() == traderapi.getTORA_TSTP_EXD_BSE())
                BSEShareholderID = pShareholderAccount.getShareholderID();
        }

        if (bIsLast) {
            log.info("华鑫查询股东账号结束!\n");
        }
    }

    public void OnRspQryTradingAccount(CTORATstpTradingAccountField pTradingAccount, CTORATstpRspInfoField pRspInfo, int nRequestID, boolean bIsLast) {
        if (pTradingAccount != null) {
            log.info("【{}（{}）】华鑫查询资金账号返回----OnRspQryTradingAccount[{}]: DepartmentID[{}] InvestorID[{}] AccountID[{}] CurrencyID[{}] UsefulMoney[{}] WithdrawQuota[{}]", cfg.getUserName(), UserID,
                    nRequestID, pTradingAccount.getDepartmentID(), pTradingAccount.getInvestorID(),
                    pTradingAccount.getAccountID(), pTradingAccount.getCurrencyID(), pTradingAccount.getUsefulMoney(),
                    pTradingAccount.getWithdraw());
            AccountID = pTradingAccount.getAccountID();
            BigDecimal posVal = BigDecimal.ZERO;
            if (positionsFinished && !positions.isEmpty()) {//持仓价值
                posVal = positions.stream().filter(p -> p.getPosition() > 0).map(Position::getCost).reduce(BigDecimal.ZERO, BigDecimal::add);
            }
            BigDecimal total = NumberUtil.add(posVal, BigDecimal.valueOf(pTradingAccount.getUsefulMoney()), BigDecimal.valueOf(pTradingAccount.getFrozenCash()), BigDecimal.valueOf(pTradingAccount.getFrozenCommission()));
            log.info("【{}（{}）】华鑫查询资金账号----持仓价值：{}，可用资金：{}，预估总额：{}", cfg.getUserName(), UserID, posVal, BigDecimal.valueOf(pTradingAccount.getUsefulMoney()), total);

            funds = Funds.builder()
                    .userId(pTradingAccount.getInvestorID())
                    .total(total)
                    .available(BigDecimal.valueOf(pTradingAccount.getUsefulMoney()))
                    .frozen(BigDecimal.valueOf(pTradingAccount.getFrozenCash()))
                    .frozenCommission(BigDecimal.valueOf(pTradingAccount.getFrozenCommission()))
                    .build();
        }

        if (bIsLast) {
            log.info("华鑫查询资金账号结束!\n");
        }
    }

    public void OnRspQryPosition(CTORATstpPositionField pPosition, CTORATstpRspInfoField pRspInfo, int nRequestID, boolean bIsLast) {
        if (pPosition != null) {
            log.info("【{}（{}）】华鑫查询持仓返回----OnRspQryPosition[{}]: InvestorID[{}] SecurityID[{}] HistoryPos[{}] TodayBSPos[{}] TodayPRPos[{}]", cfg.getUserName(), UserID,
                    nRequestID, pPosition.getInvestorID(), pPosition.getSecurityID(),
                    pPosition.getHistoryPos(), pPosition.getTodayBSPos(), pPosition.getTodayPRPos());
            if (positions == null) positions = new ArrayList<>();
            positions.add(Position.builder()
                    .userId(pPosition.getInvestorID())
                    .code(pPosition.getSecurityID())
                    .name(pPosition.getSecurityName())
                    .cost(BigDecimal.valueOf(pPosition.getTotalPosCost()))
                    .position(pPosition.getCurrentPosition())
                    .available(pPosition.getAvailablePosition())
                    .date(pPosition.getTradingDay())
                    .build());
        }

        if (bIsLast) {
            log.info("华鑫查询持仓结束!\n");
            positionsFinished = true;
            if (funds != null && !positions.isEmpty()) {
                BigDecimal posVal = positions.stream().filter(p -> p.getPosition() > 0).map(Position::getCost).reduce(BigDecimal.ZERO, BigDecimal::add);
                funds.setTotal(NumberUtil.add(posVal, funds.getAvailable(), funds.getFrozen(), funds.getFrozenCommission()));
            }
        }
    }

    public void OnRspQryOrder(CTORATstpOrderField pOrder, CTORATstpRspInfoField pRspInfo, int nRequestID, boolean bIsLast) {
        if (pOrder != null) {
            log.info("【{}（{}）】华鑫查询报单返回----OnRspQryOrder[{}]: 股票代码：{}，委托价格：{}，委托数量：{}，金额：{}  OrderLocalID[{}] OrderRef[{}] OrderSysID[{}] VolumeTraded[{}] OrderStatus报单状态[{}] StatusMsg[{}] OrderSubmitStatus报单提交状态[{}] StatusMsg[{}] InsertDate[{}] InsertTime[{}]\n", cfg.getUserName(), UserID,
                    nRequestID, pOrder.getSecurityID(), pOrder.getLimitPrice(), pOrder.getVolumeTotalOriginal(), pOrder.getLimitPrice() * pOrder.getVolumeTotalOriginal(), pOrder.getOrderLocalID(), pOrder.getOrderRef(),
                    pOrder.getOrderSysID(), pOrder.getVolumeTraded(), orderStatus(pOrder.getOrderStatus()).getName(), pOrder.getStatusMsg(),
                    pOrder.getOrderSubmitStatus(), pOrder.getStatusMsg(), pOrder.getInsertDate(), pOrder.getInsertTime());
            if (orders == null) orders = new ArrayList<>();
            if (orders.stream().noneMatch(o -> o.getCode().equals(pOrder.getSecurityID()) && ((pOrder.getDirection() == traderapi.getTORA_TSTP_D_Buy() && o.getOrderType() == OrderType.Buy) || (pOrder.getDirection() == traderapi.getTORA_TSTP_D_Sell() && o.getOrderType() == OrderType.Sell)))) {
                orders.add(Order.builder()
                        .userId(InvestorID)
                        .sysID(pOrder.getOrderSysID())
                        .localID(pOrder.getOrderLocalID())
                        .orderType(pOrder.getDirection() == traderapi.getTORA_TSTP_D_Buy() ? OrderType.Buy : (pOrder.getDirection() == traderapi.getTORA_TSTP_D_Sell()) ? OrderType.Sell : OrderType.Other)
                        .code(pOrder.getSecurityID())
                        .price(BigDecimal.valueOf(pOrder.getLimitPrice()))
                        .volume(BigDecimal.valueOf(pOrder.getVolumeTotalOriginal()))
                        .amount(BigDecimal.valueOf(pOrder.getLimitPrice() * pOrder.getVolumeTotalOriginal()))
                        .orderTime(DateUtil.parse(pOrder.getInsertDate() + " " + pOrder.getInsertTime(), "yyyyMMdd HH:mm:ss").toJdkDate())
                        .status(orderStatus(pOrder.getOrderStatus()))
                        .opTime((pOrder.getOrderStatus() == traderapi.getTORA_TSTP_OST_PartTradeCanceled() || pOrder.getOrderStatus() == traderapi.getTORA_TSTP_OST_AllCanceled() ? DateUtil.parse(pOrder.getInsertDate() + " " + pOrder.getCancelTime(), "yyyyMMdd HH:mm:ss").toJdkDate() : null))
                        .build());
            } else {
                orders.stream().filter(o -> o.getCode().equals(pOrder.getSecurityID()) && ((pOrder.getDirection() == traderapi.getTORA_TSTP_D_Buy() && o.getOrderType() == OrderType.Buy) || (pOrder.getDirection() == traderapi.getTORA_TSTP_D_Sell() && o.getOrderType() == OrderType.Sell))).forEach(o -> {
                    if (o.getSysID() == null) o.setSysID(pOrder.getOrderSysID());
                    if (o.getLocalID() == null) o.setLocalID(pOrder.getOrderLocalID());
                    if (o.getPrice() == null) o.setPrice(BigDecimal.valueOf(pOrder.getLimitPrice()));
                    if (o.getVolume() == null) o.setVolume(BigDecimal.valueOf(pOrder.getVolumeTotalOriginal()));
                    if (o.getAmount() == null)
                        o.setAmount(BigDecimal.valueOf(pOrder.getLimitPrice() * pOrder.getVolumeTotalOriginal()));
                    o.setOrderTime(DateUtil.parse(pOrder.getInsertDate() + " " + pOrder.getInsertTime(), "yyyyMMdd HH:mm:ss").toJdkDate());
                    o.setStatus(orderStatus(pOrder.getOrderStatus()));
                    if (o.getStatus() == OrderStatus.Canceled)
                        o.setOpTime(DateUtil.parse(pOrder.getInsertDate() + " " + pOrder.getCancelTime(), "yyyyMMdd HH:mm:ss").toJdkDate());
                });
            }
        }

        if (bIsLast) {
            log.info("华鑫查询报单结束!\n");
            orderFinished = true;
            executor.execute(this::queryFund);// 查询资金
            executor.execute(this::queryPositions);// 查询持仓
        }
    }

    private static OrderType tradeDirection(char direction) {
        if (direction == traderapi.getTORA_TSTP_D_Buy()) return OrderType.Buy;
        if (direction == traderapi.getTORA_TSTP_D_Sell()) return OrderType.Sell;
        return OrderType.Other;
    }

    private static OrderStatus orderStatus(char status) {
        if (status == traderapi.getTORA_TSTP_OST_Accepted() || status == traderapi.getTORA_TSTP_OST_SendTradeEngine())
            return OrderStatus.Received;
        else if (status == traderapi.getTORA_TSTP_OST_PartTradeCanceled() || status == traderapi.getTORA_TSTP_OST_AllCanceled())
            return OrderStatus.Canceled;
        else if (status == traderapi.getTORA_TSTP_OST_AllTraded())
            return (OrderStatus.Success);
        else if (status == traderapi.getTORA_TSTP_OST_PartTraded())
            return (OrderStatus.Partial);
        else if (status == traderapi.getTORA_TSTP_OST_Rejected())
            return (OrderStatus.Rejected);
        else return (OrderStatus.Unknown);
    }

    public void OnRspOrderInsert(CTORATstpInputOrderField pInputOrderField, CTORATstpRspInfoField pRspInfo, int nRequestID) {
        executor.execute(this::queryFund);// 查询资金
        executor.execute(this::queryPositions);// 查询持仓
        if (pRspInfo.getErrorID() == 0) {
            log.info("【{}（{}）】华鑫委托请求报单返回成功----OnRspOrderInsert: OK! [{}]----{}：股票代码：{}，委托价格：{}，委托数量：{}，委托编号：{}", cfg.getUserName(), UserID, nRequestID, tradeDirection(pInputOrderField.getDirection()).getName(), pInputOrderField.getSecurityID(), pInputOrderField.getLimitPrice(), pInputOrderField.getVolumeTotalOriginal(), pInputOrderField.getOrderSysID());
            if (orders.stream().noneMatch(o -> o.getCode().equals(pInputOrderField.getSecurityID()) && ((pInputOrderField.getDirection() == traderapi.getTORA_TSTP_D_Buy() && o.getOrderType() == OrderType.Buy) || (pInputOrderField.getDirection() == traderapi.getTORA_TSTP_D_Sell() && o.getOrderType() == OrderType.Sell)))) {
                orders.add(Order.builder()
                        .userId(InvestorID)
                        .orderType(pInputOrderField.getDirection() == traderapi.getTORA_TSTP_D_Buy() ? OrderType.Buy : (pInputOrderField.getDirection() == traderapi.getTORA_TSTP_D_Sell()) ? OrderType.Sell : OrderType.Other)
                        .code(pInputOrderField.getSecurityID())
                        .price(BigDecimal.valueOf(pInputOrderField.getLimitPrice()))
                        .volume(BigDecimal.valueOf(pInputOrderField.getVolumeTotalOriginal()))
                        .amount(BigDecimal.valueOf(pInputOrderField.getLimitPrice() * pInputOrderField.getVolumeTotalOriginal()))
                        .orderTime(new Date())
                        .status(OrderStatus.Submitted)
                        .build());
            } else {
                orders.stream().filter(o -> o.getCode().equals(pInputOrderField.getSecurityID()) && ((pInputOrderField.getDirection() == traderapi.getTORA_TSTP_D_Buy() && o.getOrderType() == OrderType.Buy) || (pInputOrderField.getDirection() == traderapi.getTORA_TSTP_D_Sell() && o.getOrderType() == OrderType.Sell))).forEach(o -> {
                    if (o.getSysID() == null) o.setSysID(pInputOrderField.getOrderSysID());
                    if (o.getPrice() == null) o.setPrice(BigDecimal.valueOf(pInputOrderField.getLimitPrice()));
                    if (o.getVolume() == null)
                        o.setVolume(BigDecimal.valueOf(pInputOrderField.getVolumeTotalOriginal()));
                    if (o.getAmount() == null)
                        o.setAmount(BigDecimal.valueOf(pInputOrderField.getLimitPrice() * pInputOrderField.getVolumeTotalOriginal()));
                });
            }
        } else {
            log.error("【{}（{}）】华鑫委托请求报单返回失败----OnRspOrderInsert: Error! [{}] [{}] [{}]----{}：股票代码：{}，委托价格：{}，委托数量：{}，委托编号：{}", cfg.getUserName(), UserID, nRequestID, pRspInfo.getErrorID(), pRspInfo.getErrorMsg(), tradeDirection(pInputOrderField.getDirection()).getName(), pInputOrderField.getSecurityID(), pInputOrderField.getLimitPrice(), pInputOrderField.getVolumeTotalOriginal(), pInputOrderField.getOrderSysID());
            if (pRspInfo.getErrorID() == 361) {   //数量不足

            } else if (pRspInfo.getErrorID() == 360) {   //资金不足

            }
        }
    }

    public void OnRtnOrder(CTORATstpOrderField pOrder) {
        executor.execute(this::queryFund);// 查询资金
        executor.execute(this::queryPositions);// 查询持仓
        log.info("【{}（{}）】华鑫报单回报返回----OnRtnOrder----{}：股票代码：{}，委托价格：{}，委托数量：{}，委托状态：{} OrderRef[{}] OrderLocalID[{}] OrderSysID[{}] 附加信息[{}]\n", cfg.getUserName(), UserID,
                tradeDirection(pOrder.getDirection()).getName(), pOrder.getSecurityID(), pOrder.getLimitPrice(), pOrder.getVolumeTotalOriginal(), orderStatus(pOrder.getOrderStatus()).getName(),
                pOrder.getOrderRef(), pOrder.getOrderLocalID(), pOrder.getOrderSysID(), pOrder.getSInfo());
        if (orders.stream().noneMatch(o -> o.getCode().equals(pOrder.getSecurityID()) && ((pOrder.getDirection() == traderapi.getTORA_TSTP_D_Buy() && o.getOrderType() == OrderType.Buy) || (pOrder.getDirection() == traderapi.getTORA_TSTP_D_Sell() && o.getOrderType() == OrderType.Sell)))) {
            orders.add(Order.builder()
                    .userId(InvestorID)
                    .sysID(pOrder.getOrderSysID())
                    .localID(pOrder.getOrderLocalID())
                    .orderType(pOrder.getDirection() == traderapi.getTORA_TSTP_D_Buy() ? OrderType.Buy : (pOrder.getDirection() == traderapi.getTORA_TSTP_D_Sell()) ? OrderType.Sell : OrderType.Other)
                    .code(pOrder.getSecurityID())
                    .price(BigDecimal.valueOf(pOrder.getLimitPrice()))
                    .volume(BigDecimal.valueOf(pOrder.getVolumeTotalOriginal()))
                    .amount(BigDecimal.valueOf(pOrder.getLimitPrice() * pOrder.getVolumeTotalOriginal()))
                    .orderTime(DateUtil.parse(pOrder.getInsertDate() + " " + pOrder.getInsertTime(), "yyyyMMdd HH:mm:ss").toJdkDate())
                    .status(orderStatus(pOrder.getOrderStatus()))
                    .opTime((pOrder.getOrderStatus() == traderapi.getTORA_TSTP_OST_PartTradeCanceled() || pOrder.getOrderStatus() == traderapi.getTORA_TSTP_OST_AllCanceled() ? DateUtil.parse(pOrder.getInsertDate() + " " + pOrder.getCancelTime(), "yyyyMMdd HH:mm:ss").toJdkDate() : null))
                    .build());
        } else {
            orders.stream().filter(o -> o.getCode().equals(pOrder.getSecurityID()) && ((pOrder.getDirection() == traderapi.getTORA_TSTP_D_Buy() && o.getOrderType() == OrderType.Buy) || (pOrder.getDirection() == traderapi.getTORA_TSTP_D_Sell() && o.getOrderType() == OrderType.Sell))).forEach(o -> {
                if (o.getSysID() == null) o.setSysID(pOrder.getOrderSysID());
                if (o.getLocalID() == null) o.setLocalID(pOrder.getOrderLocalID());
                if (o.getPrice() == null) o.setPrice(BigDecimal.valueOf(pOrder.getLimitPrice()));
                if (o.getVolume() == null)
                    o.setVolume(BigDecimal.valueOf(pOrder.getVolumeTotalOriginal()));
                if (o.getAmount() == null)
                    o.setAmount(BigDecimal.valueOf(pOrder.getLimitPrice() * pOrder.getVolumeTotalOriginal()));
                o.setOrderTime(DateUtil.parse(pOrder.getInsertDate() + " " + pOrder.getInsertTime(), "yyyyMMdd HH:mm:ss").toJdkDate());
                o.setStatus(orderStatus(pOrder.getOrderStatus()));
                if (o.getStatus() == OrderStatus.Canceled)
                    o.setOpTime(DateUtil.parse(pOrder.getInsertDate() + " " + pOrder.getCancelTime(), "yyyyMMdd HH:mm:ss").toJdkDate());
            });
        }
        if (pOrder.getOrderStatus() == traderapi.getTORA_TSTP_OST_Rejected() /*|| pOrder.getOrderStatus() == traderapi.getTORA_TSTP_OST_Unknown()*/) {
            if (sellStocks.containsKey(pOrder.getSInfo())) {
                log.info("【{}（{}）】华鑫报单回报返回{}----{}，重新卖出委托", cfg.getUserName(), UserID, orderStatus(pOrder.getOrderStatus()).getName(), pOrder.getIInfo());
                sell(sellStocks.get(pOrder.getSInfo()));        //交易所拒绝，重新卖出委托
            }
        }
    }

    public void OnRtnTrade(CTORATstpTradeField pTrade) {
        executor.execute(this::queryFund);// 查询资金
        executor.execute(this::queryPositions);// 查询持仓
        log.info("【{}（{}）】华鑫成交回报返回----OnRtnTrade----{}：股票代码：{}，成交价格：{}，成交数量：{}，成交金额：{}，成交时间：{}----TradeID[{}] OrderRef[{}] OrderLocalID[{}]\n", cfg.getUserName(), UserID,
                tradeDirection(pTrade.getDirection()).getName(), pTrade.getSecurityID(), pTrade.getPrice(), pTrade.getVolume(), BigDecimal.valueOf(pTrade.getPrice() * pTrade.getVolume()).setScale(2, RoundingMode.HALF_UP), pTrade.getTradeTime(),
                pTrade.getTradeID(), pTrade.getOrderRef(), pTrade.getOrderLocalID());
        if (orders.stream().noneMatch(o -> o.getCode().equals(pTrade.getSecurityID()) && ((pTrade.getDirection() == traderapi.getTORA_TSTP_D_Buy() && o.getOrderType() == OrderType.Buy) || (pTrade.getDirection() == traderapi.getTORA_TSTP_D_Sell() && o.getOrderType() == OrderType.Sell)))) {
            orders.add(Order.builder()
                    .userId(InvestorID)
                    .sysID(pTrade.getOrderSysID())
                    .localID(pTrade.getOrderLocalID())
                    .orderType(pTrade.getDirection() == traderapi.getTORA_TSTP_D_Buy() ? OrderType.Buy : (pTrade.getDirection() == traderapi.getTORA_TSTP_D_Sell()) ? OrderType.Sell : OrderType.Other)
                    .code(pTrade.getSecurityID())
                    .price(BigDecimal.valueOf(pTrade.getPrice()))
                    .volume(BigDecimal.valueOf(pTrade.getVolume()))
                    .amount(BigDecimal.valueOf(pTrade.getPrice() * pTrade.getVolume()))
                    .orderTime(DateUtil.parse(pTrade.getTradeDate() + " " + pTrade.getTradeTime(), "yyyyMMdd HH:mm:ss").toJdkDate())
                    .status(OrderStatus.Success)
                    .opTime(DateUtil.parse(pTrade.getTradeDate() + " " + pTrade.getTradeTime(), "yyyyMMdd HH:mm:ss").toJdkDate())
                    .build());
        } else {
            orders.stream().filter(o -> o.getCode().equals(pTrade.getSecurityID()) && ((pTrade.getDirection() == traderapi.getTORA_TSTP_D_Buy() && o.getOrderType() == OrderType.Buy) || (pTrade.getDirection() == traderapi.getTORA_TSTP_D_Sell() && o.getOrderType() == OrderType.Sell))).forEach(o -> {
                if (o.getSysID() == null) o.setSysID(pTrade.getOrderSysID());
                if (o.getLocalID() == null) o.setLocalID(pTrade.getOrderLocalID());
                if (o.getPrice() == null) o.setPrice(BigDecimal.valueOf(pTrade.getPrice()));
                if (o.getVolume() == null)
                    o.setVolume(BigDecimal.valueOf(pTrade.getVolume()));
                if (o.getAmount() == null)
                    o.setAmount(BigDecimal.valueOf(pTrade.getPrice() * pTrade.getVolume()));
                if (o.getOrderTime() == null)
                    o.setOrderTime(DateUtil.parse(pTrade.getTradeDate() + " " + pTrade.getTradeTime(), "yyyyMMdd HH:mm:ss").toJdkDate());
                o.setStatus(OrderStatus.Success);
                o.setOpTime(DateUtil.parse(pTrade.getTradeDate() + " " + pTrade.getTradeTime(), "yyyyMMdd HH:mm:ss").toJdkDate());
            });
        }
    }

    public void OnRspOrderAction(CTORATstpInputOrderActionField pInputOrderActionField, CTORATstpRspInfoField pRspInfo, int nRequestID) {
        executor.execute(this::queryFund);// 查询资金
        executor.execute(this::queryPositions);// 查询持仓
        if (pRspInfo.getErrorID() == 0) {
            log.info("【{}（{}）】华鑫撤单响应成功----OnRspOrderAction: OK! [{}]----编号：{}\n", cfg.getUserName(), UserID, nRequestID, pInputOrderActionField.getOrderSysID());
        } else {
            log.info("【{}（{}）】华鑫撤单响应失败----OnRspOrderAction: Error! [{}] [{}] [{}]\n", cfg.getUserName(), UserID, nRequestID, pRspInfo.getErrorID(), pRspInfo.getErrorMsg());
        }
    }

    public void OnErrRtnOrderAction(CTORATstpInputOrderActionField pInputOrderActionField, CTORATstpRspInfoField pRspInfo, int nRequestID) {
        executor.execute(this::queryFund);// 查询资金
        executor.execute(this::queryPositions);// 查询持仓
        if (pRspInfo.getErrorID() == 0) {
            log.info("【{}（{}）】华鑫撤单错误回报成功----OnRspOrderAction: OK! [{}]\n", cfg.getUserName(), UserID, nRequestID);
        } else {
            log.info("【{}（{}）】华鑫撤单错误回报失败----OnRspOrderAction: Error! [{}] [{}] [{}]\n", cfg.getUserName(), UserID, nRequestID, pRspInfo.getErrorID(), pRspInfo.getErrorMsg());
        }
    }

    public void OnRspTransferFund(CTORATstpInputTransferFundField pInputTransferFundField, CTORATstpRspInfoField pRspInfo, int nRequestID) {
        executor.execute(this::queryFund);// 查询资金
//        executor.execute(this::queryPositions);// 查询持仓
        if (pRspInfo.getErrorID() == 0) {
            log.info("【{}（{}）】华鑫资金转移返回----OnRspTransferFund: OK! [{}]\n", cfg.getUserName(), UserID, nRequestID);
        } else {
            log.info("【{}（{}）】华鑫资金转移返回----OnRspTransferFund: Error! [{}] [{}] [{}]\n", cfg.getUserName(), UserID, nRequestID, pRspInfo.getErrorID(), pRspInfo.getErrorMsg());
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        TradeHuaXinCfg xw = TradeHuaXinCfg.builder().userName("谢炜华鑫模拟账号").userId("00646531").password("63304162").maxBuyCntPerDay(1).buyStrategy("shangbang_buy").sellStrategy("fall_avg").build();
        TradeHuaXinCfg xf = TradeHuaXinCfg.builder().userName("徐樊华鑫模拟账号").userId("00361630").password("95094485").maxBuyCntPerDay(10).buyStrategy("zhangting_buy").sellStrategy("fall_avg").build();

        StockTradeHuaXin huaXin = StockTradeHuaXin.init(xf);

        Thread.sleep(2000);
        System.out.println(huaXin.queryPositions());
//        Stock stock = new Stock().code("603131").name("上海沪工");
//        stock.setJiage("15.30");
//        huaXin.buy(stock);
        List<Stock> stocks = new StockList().mergeThs();
        Stock stock = stocks.stream().filter(s -> s.getStock_code().equals("002351")).findFirst().get();
        huaXin.sell(stock);
        Thread.sleep(20000);
        Funds funds = huaXin.queryFund();
        List<Order> orders = huaXin.queryOrder();
        System.out.println(funds);
        System.out.println(orders);

    }
}